﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.IO;
using System.Diagnostics;
using System.Windows.Threading;
using System.Xml;
using System.Windows.Markup;
using System.Management;
using EnterpriseDataBackuper.Core;

namespace EnterpriseDataBackuper
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private readonly object _dummyNode = null;

        private SyncEngine syncEngine;
        private List<KeyValuePair<KeyValuePair<string, string>, KeyValuePair<string, string>>> locations;
        private List<KeyValuePair<KeyValuePair<string, string>, KeyValuePair<string, string>>> processedLocations;

        private String backupPath;

        private bool forceRestore = false, forceBackup = false;

        public MainWindow()
        {
            syncEngine = new SyncEngine();
            locations = new List<KeyValuePair<KeyValuePair<string, string>, KeyValuePair<string, string>>>();

            AddInsertUSBHandler();
            AddRemoveUSBHandler();

            WindowStartupLocation = System.Windows.WindowStartupLocation.CenterScreen;
            WindowStyle = WindowStyle.ToolWindow;
            InitializeComponent();
            Load();
            this.Loaded += new RoutedEventHandler(Window1_Loaded);
            TrayHide();
        }

        static TreeViewItem VisualUpwardSearch(DependencyObject source)
        {
            while (source != null && !(source is TreeViewItem))
                source = VisualTreeHelper.GetParent(source);

            return source as TreeViewItem;
        }

        private void TreeViewItem_PreviewMouseRightButtonDown(object sender, MouseEventArgs e)
        {
            TreeViewItem item = VisualUpwardSearch(e.OriginalSource as DependencyObject);
            if (item != null)
            {
                Console.WriteLine("Klikłeś");
                item.Focus();
                if (TreeViewItemProps.GetIsSynchronized(item) == false)
                {
                    TreeViewItemProps.SetIsSynchronized(item, true);
                    //var path = (item.Tag as string).ToCharArray();
                    //path[0] = 'E';
                    //Console.WriteLine(path);

                    String sourcePath = (item.Tag as string).Trim();

                    if (!(new DirectoryInfo(sourcePath).Exists))
                    {
                        var parent = new DirectoryInfo(sourcePath).Parent;
                        sourcePath = parent.FullName;
                    }
                    else

                    Console.WriteLine("Source path : " + sourcePath);
                    String destinationPath = backupPath.Trim() + "backup\\" + sourcePath.Substring(3, sourcePath.Length - 3);
                    Console.WriteLine("Destination path : " + destinationPath);

                    KeyValuePair<string, string> location = new KeyValuePair<string, string>(sourcePath, destinationPath);
                    KeyValuePair<string, string> sourceAndDestGuid = new KeyValuePair<string, string>(null, null);
                    KeyValuePair<KeyValuePair<string, string>, KeyValuePair<string, string>> locationWithGuid = new KeyValuePair<KeyValuePair<string, string>, KeyValuePair<string, string>>(location, sourceAndDestGuid);
                    locations.Add(locationWithGuid);
                }
                else
                {
                    KeyValuePair<KeyValuePair<string, string>, KeyValuePair<string, string>> toRemove = new KeyValuePair<KeyValuePair<string, string>, KeyValuePair<string, string>>();
                    foreach (KeyValuePair<KeyValuePair<string, string>, KeyValuePair<string, string>> locationWithGuid in locations)
                    {
                        if (locationWithGuid.Key.Key.Equals(item.Tag as string))
                        {
                            toRemove = locationWithGuid;
                        }
                    }
                    locations.Remove(toRemove);
                    TreeViewItemProps.SetIsSynchronized(item, false);
                }
                filePath.Text = item.Tag as string;
                e.Handled = true;
            }
        }

        void Window1_Loaded(object sender, RoutedEventArgs e)
        {
            foreach (string drive in Directory.GetLogicalDrives())
            {
                TreeViewItem item = new TreeViewItem();
                item.Header = drive;
                item.Tag = drive;
                item.Items.Add(_dummyNode);
                item.Expanded += folder_Expanded;

                // Apply the attached property so that 
                // the triggers know that this is root item.
                TreeViewItemProps.SetIsRootLevel(item, true);
                foldersTree.Items.Add(item);
            }
        }

        void folder_Expanded(object sender, RoutedEventArgs e)
        {
            TreeViewItem item = (TreeViewItem)sender;
            if (item.Items.Count == 1 && item.Items[0] == _dummyNode)
            {
                item.Items.Clear();

                try
                {
                    foreach (string file in Directory.GetFiles(item.Tag as string))
                    {
                        TreeViewItem subitem = new TreeViewItem();
                        subitem.Header = new DirectoryInfo(file).Name;
                        subitem.Tag = file;
                        if ((File.GetAttributes(file) & FileAttributes.Directory) != FileAttributes.Directory)
                            TreeViewItemProps.SetIsFile(subitem, true);

                        if (isSynchronized(file))
                        {
                            TreeViewItemProps.SetIsSynchronized(subitem, true);
                        }

                        item.Items.Add(subitem);
                    }
                }
                catch (Exception) { }

                try
                {
                    foreach (string dir in Directory.GetDirectories(item.Tag as string))
                    {
                        TreeViewItem subitem = new TreeViewItem();
                        subitem.Header = new DirectoryInfo(dir).Name;
                        subitem.Tag = dir;
                        subitem.Items.Add(_dummyNode);
                        subitem.Expanded += folder_Expanded;
                        TreeViewItemProps.SetIsFile(item, false);
                        if (isSynchronized(dir))
                        {
                            TreeViewItemProps.SetIsSynchronized(subitem, true);
                        }
                        item.Items.Add(subitem);
                    }
                }
                catch (Exception) { }

            }
        }

        private bool isSynchronized(String path)
        {
            foreach (KeyValuePair<KeyValuePair<string, string>, KeyValuePair<string, string>> locationWithGuid in locations)
            {
                if (locationWithGuid.Key.Key.Equals(path))
                {
                    return true;
                }
            }
            return false;
        }

        //load path locations
        public void Load()
        {
            if (!File.Exists(Properties.Resources.confFile))
            {
                using (FileStream fileStream = new FileStream(Properties.Resources.confFile, FileMode.Create, FileAccess.ReadWrite))
                {
                }
                return;
            }
            else
            {
                using (StreamReader reader = new StreamReader(Properties.Resources.confFile))
                {
                    string line;
                    string[] row;

                    while ((line = reader.ReadLine()) != null && line != "")
                    {
                        row = line.Split(',');
                        //source, destination

                        KeyValuePair<string, string> location = new KeyValuePair<string, string>(row[0].Trim(), row[1].Trim());
                        KeyValuePair<string, string> sourceAndDestGuid = new KeyValuePair<string, string>(row[2].Trim(), row[3].Trim());
                        KeyValuePair<KeyValuePair<string, string>, KeyValuePair<string, string>> locationWithGuid = new KeyValuePair<KeyValuePair<string, string>, KeyValuePair<string, string>>(location, sourceAndDestGuid);
                        locations.Add(locationWithGuid);
                    }
                }
                //pass on locations to sync iterator
            }
        }

        // Tray
        private void TrayLeftMouseUp(object sender, RoutedEventArgs e)
        {
            if (WindowState == WindowState.Minimized)
                TrayRestore();
            else
                TrayHide();
        }

        //Same as double click on icon, who cares...
        private void TraySettings(object sender, RoutedEventArgs e)
        {
            if (this.WindowState == System.Windows.WindowState.Minimized)
                ChangeWindowState(true, System.Windows.WindowState.Normal);
        }

        private void TrayHide()
        {
            ChangeWindowState(false, System.Windows.WindowState.Minimized);
        }

        private void TrayRestore()
        {
            ChangeWindowState(true, System.Windows.WindowState.Normal);
        }

        private void TrayQuit(object sender, RoutedEventArgs e)
        {
            ExitApplication(sender, null);
        }

        private void ExitApplication(object sender, RoutedEventArgs e)
        {
            w.Stop();
            w2.Stop();

            if (null != processedLocations)
            {
                //save locationsWithGuid list
                using (FileStream fileStream = File.Open("conf.dat", FileMode.Create))
                {
                    using (StreamWriter streamWriter = new StreamWriter(fileStream))
                    {
                        foreach (KeyValuePair<KeyValuePair<string, string>, KeyValuePair<string, string>> locationWithGuid in processedLocations)
                        {
                            streamWriter.WriteLine(locationWithGuid.Key.Key + ", " + locationWithGuid.Key.Value + ", " + locationWithGuid.Value.Key + ", " + locationWithGuid.Value.Value);
                        }
                    }
                }
            }

            NotifyIcon.Dispose();
            Application.Current.Shutdown();
        }

        private void ChangeWindowState(bool showInTaskbar, System.Windows.WindowState newState)
        {
            this.WindowState = newState;
            this.ShowInTaskbar = showInTaskbar;
        }

        private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            e.Cancel = true;
            ChangeWindowState(false, System.Windows.WindowState.Minimized);
        }

        #region TreeView Control

        #endregion

        object CloneUsingXaml(object obj)
        {
            string xaml = XamlWriter.Save(obj);
            return XamlReader.Load(new XmlTextReader(new StringReader(xaml)));
        }

        private void button1_Click(object sender, RoutedEventArgs e)
        {
            System.Windows.Forms.OpenFileDialog dialog = new System.Windows.Forms.OpenFileDialog();
            dialog.InitialDirectory = "C:\\";

            System.Windows.Forms.DialogResult result = dialog.ShowDialog();
            filePath.Text = dialog.FileName;

        }

        private void button2_Click(object sender, RoutedEventArgs e)
        {
            System.Windows.Forms.FolderBrowserDialog dialog = new System.Windows.Forms.FolderBrowserDialog();
            System.Windows.Forms.DialogResult result = dialog.ShowDialog();
            filePath.Text = dialog.SelectedPath;
        }

        private void backupButton_Click(object sender, RoutedEventArgs e)
        {
            processedLocations = new List<KeyValuePair<KeyValuePair<string, string>, KeyValuePair<string, string>>>();

            foreach (KeyValuePair<KeyValuePair<string, string>, KeyValuePair<string, string>> locationWithGuid in locations)
            {
                string sourceGuid, destinationGuid;
                String locationPath = backupPath + locationWithGuid.Key.Value.Substring(3, locationWithGuid.Key.Value.Length - 3);

                if (forceBackup)
                {
                    if (new DirectoryInfo(locationWithGuid.Key.Value).Exists)
                    {
                        Directory.Delete(locationWithGuid.Key.Value, true);
                        DirectoryInfo d = new DirectoryInfo(locationWithGuid.Key.Value);
                        d.Create();
                        System.Threading.Thread.Sleep(500);
                    }
                    else
                    {
                        //file nigdy tu nie powinien wejsc
                        File.Delete(locationWithGuid.Key.Value);
                    }
                }

                sourceGuid = syncEngine.configureSourceProvider(locationWithGuid.Key.Key, locationWithGuid.Value.Key);
                destinationGuid = syncEngine.configureDestinationProvider(locationWithGuid.Key.Key, locationPath, locationWithGuid.Value.Value);                
               
                processedLocations.Add(new KeyValuePair<KeyValuePair<string, string>, KeyValuePair<string, string>>(
                    new KeyValuePair<string, string>(locationWithGuid.Key.Key, locationPath),
                    new KeyValuePair<string, string>(sourceGuid, destinationGuid)
                    ));
                syncEngine.CreateBackup();
            }
        }

        private void restoreButton_Click(object sender, RoutedEventArgs e)
        {
            processedLocations = new List<KeyValuePair<KeyValuePair<string, string>, KeyValuePair<string, string>>>();

            foreach (KeyValuePair<KeyValuePair<string, string>, KeyValuePair<string, string>> locationWithGuid in locations)
            {
                if (forceRestore)
                {
                    if (new DirectoryInfo(locationWithGuid.Key.Key).Exists)
                    {
                        Directory.Delete(locationWithGuid.Key.Key, true);
                        DirectoryInfo d = new DirectoryInfo(locationWithGuid.Key.Key);
                        d.Create();
                        System.Threading.Thread.Sleep(500);
                    }
                    else
                    {
                        //file tu tez :)
                        File.Delete(locationWithGuid.Key.Key);
                    }
                }

                string sourceGuid, destinationGuid;
                sourceGuid = syncEngine.configureSourceProvider(locationWithGuid.Key.Key, locationWithGuid.Value.Key);
                String locationPath = backupPath + locationWithGuid.Key.Value.Substring(3, locationWithGuid.Key.Value.Length - 3);
                destinationGuid = syncEngine.configureDestinationProvider(locationWithGuid.Key.Key, locationPath, locationWithGuid.Value.Value);

                processedLocations.Add(new KeyValuePair<KeyValuePair<string, string>, KeyValuePair<string, string>>(
                    new KeyValuePair<string, string>(locationWithGuid.Key.Key, locationPath),
                    new KeyValuePair<string, string>(sourceGuid, destinationGuid)
                    ));

                Console.WriteLine("Restoring to {0}", locationWithGuid.Key.Key);

                syncEngine.Restore();
            }
        }

        #region USBevent

        static ManagementEventWatcher w = null;
        static ManagementEventWatcher w2 = null;

        private void AddRemoveUSBHandler()
        {

            WqlEventQuery q;
            ManagementScope scope = new ManagementScope("root\\CIMV2");
            scope.Options.EnablePrivileges = true;

            try
            {

                q = new WqlEventQuery();
                q.EventClassName = "__InstanceDeletionEvent";
                q.WithinInterval = new TimeSpan(0, 0, 3);
                q.Condition = "TargetInstance ISA 'Win32_USBControllerdevice'";
                w2 = new ManagementEventWatcher(scope, q);
                w2.EventArrived += USBRemoved;

                w2.Start();
            }
            catch (Exception e)
            {


                Console.WriteLine(e.Message);
                if (w2 != null)
                {
                    w2.Stop();

                }
            }

        }

        private void AddInsertUSBHandler()
        {

            WqlEventQuery q;
            ManagementScope scope = new ManagementScope("root\\CIMV2");
            scope.Options.EnablePrivileges = true;

            try
            {

                q = new WqlEventQuery();
                q.EventClassName = "__InstanceCreationEvent";
                q.WithinInterval = new TimeSpan(0, 0, 3);
                q.Condition = "TargetInstance ISA 'Win32_USBControllerdevice'";
                w = new ManagementEventWatcher(scope, q);
                w.EventArrived += USBInserted;

                w.Start();
            }
            catch (Exception e)
            {

                Console.WriteLine(e.Message);
                if (w != null)
                {
                    w.Stop();

                }
            }
        }

        private void USBInserted(object sender, EventArrivedEventArgs e)
        {
            foreach (DriveInfo d in DriveInfo.GetDrives())
            {
                if (d.IsReady && d.DriveType == DriveType.Removable)
                    Console.WriteLine("Plug {0} USB removable device", d.Name);
                backupPath = d.Name;
            }

            this.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Background, new System.Windows.Threading.DispatcherOperationCallback(
              delegate
              {
                  TrayRestore();
                  return null;
              }), null);
        }

        private void USBRemoved(object sender, EventArgs e)
        {
            Console.WriteLine("A USB device removed");
          this.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Background, new System.Windows.Threading.DispatcherOperationCallback(
              delegate 
              {
                  TrayHide();
                  return null;
              }), null);
        }
        #endregion

        private void backupCheckBox_Checked(object sender, RoutedEventArgs e)
        {
            forceBackup = true;
        }

        private void backupCheckBox_Unchecked(object sender, RoutedEventArgs e)
        {
            forceBackup = false;
        }

        private void restoreCheckBox_Checked(object sender, RoutedEventArgs e)
        {
            forceRestore = true;
        }

        private void restoreCheckBox_Unchecked(object sender, RoutedEventArgs e)
        {
            forceRestore = false;
        }
    }

}
